#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "MSTK.h"
#include <mpi.h>

int main(int argc, char *argv[]) {
  int i, ebit, ok, dim;
  char mname[256],filename[256];
  char buf[256];
  char subfilename[256];
  Mesh_ptr mesh, recv_mesh, *submeshes;
  RepType rtype;
  /* default divide into two parts */
  int num = 2;
  int rank;
  Global_Info *global_info = (Global_Info*)MSTK_malloc(sizeof(Global_Info *));
  /* command line parameter */
  MPI_Init(&argc,&argv);
  MPI_Comm_size(MPI_COMM_WORLD,&num);
  MPI_Comm_rank(MPI_COMM_WORLD,&rank);

  /* Build the global attributes list */
  char attname[256];
  MAttrib_ptr attrib;
  List_ptr attrib_list = List_New(2);
  strcpy(attname,"coordinates");
  attrib = MAttrib_New(NULL,attname,VECTOR,MVERTEX,3);
  List_Add(attrib_list,attrib);

  strcpy(attname,"Face_PType");
  attrib = MAttrib_New(NULL,attname,INT,MFACE);
  List_Add(attrib_list,attrib);

  /* initialize MPI data type */
  MESH_InitializeMPI(global_info,num);
  


  /* Load the mesh */
  strcpy(mname,argv[1]);
  strcpy(filename,mname);
  strcat(filename,".mstk");
  dim = atoi(argv[3]);    
  /* master process read in and divide the mesh */
  if(rank == 0) { 
    /* Initialize MSTK - Always do this even if it does not seem to
    matter in this version of MSTK */
    MSTK_Init();  
    mesh = MESH_New(UNKNOWN_REP);
    ok = MESH_InitFromFile(mesh,filename);
    if (!ok) {
      fprintf(stderr,"Cannot open input file %s\n\n\n",filename);
      exit(-1);
    }
    fprintf(stdout,"mstk mesh %s read in successfully\n",filename);
    
    /* initialze attributes*/
    MESH_InitializeAttr(mesh,attrib_list);

    /* Partition the mesh*/
    rtype = MESH_RepType(mesh);
    submeshes = (Mesh_ptr *)MSTK_malloc((num)*sizeof(Mesh_ptr));
    ok = MESH_Partition(mesh,submeshes,num);
    fprintf(stdout,"%s has been partitioned into %d submeshes\n",filename,num);

    /* operation on submeshes */
    for(i = 0; i < num; i++) {  
      /* initialze attributes*/
      MESH_InitializeAttr(submeshes[i],attrib_list);
      
      /* built ptype */
      MESH_BuildPBoundary(mesh,submeshes[i]);
      /* add ghost */
      MESH_AddGhost(mesh,submeshes[i],i,1);
      if(i > 0) {
	/* send mesh */
	MESH_SendMesh(submeshes[i],i,MPI_COMM_WORLD);
	/* send coordinate */
	MESH_SendAttr(submeshes[i], 0, i, MPI_COMM_WORLD);
      }
    }
  }

  /* receive mesh */
  if(rank == 0)
    /* submesh on the root */
    recv_mesh = submeshes[0];
  else {
    recv_mesh = MESH_New(R1);
    MESH_RecvMesh(recv_mesh,dim,0,rank,MPI_COMM_WORLD);
    fprintf(stdout,"submesh %d received\n",rank);
    /* initialze attributes*/
    MESH_InitializeAttr(recv_mesh,attrib_list);
    /* receive coordinates */
    MESH_RecvAttr(recv_mesh, 0, 0, rank, MPI_COMM_WORLD);
  }

  /* build ghost list */
  MESH_BuildGhostList(recv_mesh,0);

  /* update ghost info */
  MESH_UpdateGhostInfo(recv_mesh, global_info, rank, num,  MPI_COMM_WORLD);  
  for(i = 0; i < num; i++) {
    ebit = global_info->global_ranks[rank*num+i];
    printf("on rank %d i %d ebit:%d\n",rank, i, ebit);
    if( (ebit & 1) )
      printf("mesh %d has ghost vertex on processor %d\n",rank, i);
    if( (ebit>>1) & 1 )
      printf("mesh %d has utilized vertex on processor %d\n",rank, i);
    if( (ebit>>2) & 1 )
      printf("mesh %d has ghost edge on processor %d\n",rank, i);
    if( (ebit>>3) & 1 )
      printf("mesh %d has utilized edge on processor %d\n",rank, i);
    if( (ebit>>4) & 1 )
      printf("mesh %d has ghost face on processor %d\n",rank, i);
    if( (ebit>>5) & 1 )
      printf("mesh %d has utilized face on processor %d\n",rank, i);
    if( (ebit>>6) & 1 )
      printf("mesh %d has ghost region on processor %d\n",rank, i);
    if( (ebit>>7) & 1 )
      printf("mesh %d has utilized region on processor %d\n",rank, i);
  }


  strcpy(subfilename,mname);
  itoa(rank,buf);
  strcat(subfilename,buf);
  strcat(subfilename,".mstk");
  PMESH_WriteToFile(recv_mesh,subfilename,R1);

  /* clean up */
  MPI_Barrier(MPI_COMM_WORLD);

  MESH_FinalizeMPI(global_info);
  MPI_Finalize();
  return 1;
}

